/*-+-*****************************************************************************
 Copyright (C) 2009-2012 Oceanside Software Corporation (R)
 Dallas, Texas

 Author       : Jason T. Brower
 Purpose      : This file is part of a point-of-sale software package
                originally written for Table-Service Restaurants.
              
 Date         : July 8th of 2012
              
 History      : The application that this software file belongs to was 
                written over a 3-Year period.  The first version was written
                in C# .Net 3.5 using Windows Forms.  Being new to .Net at
                the time, I soon learned of Windows Presentation Foundation,
                liked the graphics capabilities and databinding so I
                migrated to WPF.  When the .Net 4.0 framework was released
                the application was migrated from 3.5 to 4.0. Now today,
                .Net 4.5 RC is out and I was already in process of migrating
                before I made this public.
                
Excuse Me     : Please excuse the coding style, it is not representative of
                my typical work.  During the three years I was under a very
                tight budget, constantly running out of funding and was 
                working 18 hour days hyped up on 5-Hour Energy.  The 
                design is not textbook.  It is monolythic because of
                previous issues I will not get into.  There are few comments
                in the code and the ones that are there may be incorrect.
                
What it is not: This is not the exact version that I took through PA-DSS 2.0
                nor is it the exact version running at my customer sites.
                I removed some details for security purposes including some
                encryption algorithms.  The reporting was removed because
                I was including a third party library that cannot be 
                published with this Open Source.
                
What it is    : This application has been running at high volume restaraunts
                for over a year.  It never freezes, crashes or does anything
                unexpected.  So long as it is built, installed and configured
                correctly, it is a strong system that we can all enhance.

Contact       : Please note that I can no longer work for free.  However, you
                can reach me at information@oceansidesw.com.                                                      

*********************************************************************************/
using System;
using System.Text.RegularExpressions;
#if _LOCALDB
  using Oceanside.LocalDB;
#else
using Oceanside.RemoteDB;
#endif
namespace Oceanside.Payments
{
    //Track 1
    //The Track 1 structure is specified as:
    //    * STX : Start sentinel "%"
    //    * FC : Format code "B" (The format described here. Format "A" is reserved for proprietary use.)
    //    * PAN : Primary Account Number, up to 19 digits
    //    * FS : Separator "^"
    //    * NM : Name, 2 to 26 characters (including separators, where appropriate, between surname, first Name etc)
    //    * FS : Separator "^"
    //    * ED : Expiration data, 4 digits or "^"
    //    * SC : Service code, 3 digits or "^"
    //    * DD : Discretionary data, balance of characters
    //    * ETX : End sentinel "?"
    //    * LRC : Longitudinal redundancy check, calculated according to ISO/IEC 7811-2
    //The maximum record length is 79 alphanumeric characters.

    //Track 2
    //The Track 2 structure is specified as:
    //    * STX : Start sentinel ";"
    //    * PAN : Primary Account Number, up to 19 digits, as defined in ISO/IEC 7812-1
    //    * FS : Separator "="
    //    * ED : Expiration date, YYMM or "=" if not present
    //    * SC : Service code, 3 digits or "=" if not present
    //    * DD : Discretionary data, balance of available digits
    //    * ETX : End sentinel "?"
    //    * LRC : Longitudinal redundancy check, calculated according to ISO/IEC 7811-2
    //The maximum record length is 40 numeric digits.
    public class MagneticCard
    {
        public string Name = "";
        public string Pan = null;
        public string Expire = null;
        public string TrackTwo = null;
        public string TrackOne = null;
        public string CardType = null;
        public bool IsKeyed = false;
        public bool IsValid = false;

        /// <summary>
        /// The following constants are used to decode the magnetic stripe tracks
        /// </summary>
        public static string T1_STARTSENTINEL_AND_CODE = "%B";
        public static string T2_STARTSENTINEL = ";";
        private static string T2_PAN_END_SENTINEL = "=";
        public static string ENDSENTINEL = "?";
        public static string SEPERATOR = "^";
        public static int EXPIRE_LEN = 4;

        public MagneticCard(string frameString, bool isTracktwo = false)
        {
            string origFrame = frameString;
            try
            {
                if (!isTracktwo)
                {
                    ///Grab Track Two.  Not all cards will have track one.
                    int t2StartIdx = origFrame.IndexOf(T2_STARTSENTINEL);
                    if (t2StartIdx >= 0)
                    {
                        int t2EndIdx = origFrame.IndexOf(ENDSENTINEL, t2StartIdx);
                        if (t2EndIdx > t2StartIdx)
                        {
                            TrackTwo = origFrame.Substring(t2StartIdx + 1, t2EndIdx - t2StartIdx - 1);

                            int t2PanEndIdx = origFrame.IndexOf(T2_PAN_END_SENTINEL, t2StartIdx);
                            if (t2PanEndIdx > t2StartIdx)
                            {
                                Pan = origFrame.Substring(t2StartIdx + 1, t2PanEndIdx - t2StartIdx - 1);
                                CardType = GetCardType(Pan);

                                int t2ExpireStartIdx = origFrame.IndexOf(T2_PAN_END_SENTINEL) + 1;
                                int t2ExpireEndIdx = t2StartIdx + EXPIRE_LEN;

                                ///Get the expiration date, then swap the YYMM to MMYY because that is what the gateway requires.
                                this.Expire = frameString.Substring(t2ExpireStartIdx + 2, 2) + frameString.Substring(t2ExpireStartIdx, 2);

                                ///Trim the spaces off (should never be any.
                                this.Pan.Trim();
                                this.Expire.Trim();
                            }
                            ///All is good.
                            IsValid = true;
                        }
                    }
                    ///Now see if we can find a track one.
                    int t1StartIndex = frameString.IndexOf(T1_STARTSENTINEL_AND_CODE);
                    int t1EndIndex = frameString.IndexOf(ENDSENTINEL);
                    if (t1StartIndex == 0 && t1EndIndex > 0)
                    {
                        int t1NameStartIdx = frameString.IndexOf(SEPERATOR);
                        frameString = frameString.Substring(t1NameStartIdx + 1, frameString.Length - (t1NameStartIdx + 1));

                        ///Get the cardholders Name.
                        int t1NameEndIndex = frameString.IndexOf(SEPERATOR);
                        this.Name = frameString.Substring(0, t1NameEndIndex);

                        ///Trim the name spaces.
                        this.Name.Trim();

                        TrackOne = origFrame.Substring(t1StartIndex + 2, t1EndIndex - 1);

                    }
                }
                else
                {
                    this.TrackTwo = frameString;
                    int t2PanEndIdx = origFrame.IndexOf(T2_PAN_END_SENTINEL);

                    Pan = origFrame.Substring(0, t2PanEndIdx);
                    CardType = GetCardType(Pan);

                    int t2ExpireStartIdx = origFrame.IndexOf(T2_PAN_END_SENTINEL) + 1;
                    int t2ExpireEndIdx = EXPIRE_LEN;

                    ///Get the expiration date, then swap the YYMM to MMYY because that is what the gateway requires.
                    this.Expire = frameString.Substring(t2ExpireStartIdx + 2, 2) + frameString.Substring(t2ExpireStartIdx, 2);

                    ///Trim the spaces off (should never be any.
                    this.Pan.Trim();
                    this.Expire.Trim();
                    this.TrackTwo.Trim();
                }
                ///All is good.
                IsValid = true;
            }
            catch (Exception e)
            {
                Logging.LogException(e);
            }
        }

        public MagneticCard(string pan, string expire)
        {
            try
            {
                if (!string.IsNullOrEmpty(pan)) ///Pan Can be null for gift cards.
                {
                    IsKeyed = true;
                    Pan = pan;
                    Expire = expire;
                    CardType = GetCardType(Pan);
                    IsValid = true;
                }
            }
            catch (Exception e)
            {
                Logging.LogException(e);
            }
        }
        public const string CONST_MERCURY_GIFT_STRING = "Merc/Gift";
        public string GetCardType(string number)
        {
            string cardType = "OTHER";

            if (!Regex.IsMatch(number, @"[^\d\s]"))
            {
                if (number.Length > 13)
                {

                    if (number.Substring(0, 7).Contains("6050110"))
                    {
                        cardType = CONST_MERCURY_GIFT_STRING;
                    }
                    else if (Regex.IsMatch(number, @"^4\d{12}(\d{3})?$", RegexOptions.Compiled))
                    {
                        cardType = "VISA";
                    }
                    else if (Regex.IsMatch(number, @"^5[1-5]\d{14}$", RegexOptions.Compiled))
                    {
                        cardType = "M/C";
                    }
                    else if (Regex.IsMatch(number, @"^6011\d{12}$", RegexOptions.Compiled))
                    {
                        cardType = "DCVR";
                    }
                    else if (Regex.IsMatch(number, @"^3[47]\d{13}$", RegexOptions.Compiled))
                    {
                        cardType = "AMEX";
                    }
                    else if (Regex.IsMatch(number, @"^3(0[0-5]|[68]\d)\d{11}$", RegexOptions.Compiled))
                    {
                        cardType = "DCLB";
                    }
                    else if (Regex.IsMatch(number, @"^2(014|149)\d{11}$", RegexOptions.Compiled))
                    {
                        //cardType = "enRoute";
                        cardType = "OTHER";
                    }
                    else if (Regex.IsMatch(number, @"^(3\d{4}|2131|1800)\d{11}$", RegexOptions.Compiled))
                    {
                        cardType = "JCB";
                    }
                    else if (Regex.IsMatch(number, @"^56(10\d\d|022[1-5])\d{10}$", RegexOptions.Compiled))
                    {
                        //cardType = "BankCard";
                        cardType = "OTHER";
                    }
                }
            }

            return cardType;
        }

        public static int GetTenderType(string number)
        {
            int cardType = TRANSACTION.CONST_TENDER_OTHER;

            if (!Regex.IsMatch(number, @"[^\d\s]"))
            {
                if (number.Length > 13)
                {
                    if (number.Substring(0, 7).Contains("6050110"))
                    {
                        cardType = TRANSACTION.CONST_TENDER_GIFT;
                    }
                    else if (Regex.IsMatch(number, @"^4\d{12}(\d{3})?$", RegexOptions.Compiled))
                    {
                        cardType = TRANSACTION.CONST_TENDER_VISA;
                    }
                    else if (Regex.IsMatch(number, @"^5[1-5]\d{14}$", RegexOptions.Compiled))
                    {
                        cardType = TRANSACTION.CONST_TENDER_MC;
                    }
                    else if (Regex.IsMatch(number, @"^6011\d{12}$", RegexOptions.Compiled))
                    {
                        cardType = TRANSACTION.CONST_TENDER_DISC;
                    }
                    else if (Regex.IsMatch(number, @"^3[47]\d{13}$", RegexOptions.Compiled))
                    {
                        cardType = TRANSACTION.CONST_TENDER_AMEX;
                    }
                    else if (Regex.IsMatch(number, @"^3(0[0-5]|[68]\d)\d{11}$", RegexOptions.Compiled))
                    {
                        cardType = TRANSACTION.CONST_TENDER_DNCLB;
                    }
                    else if (Regex.IsMatch(number, @"^2(014|149)\d{11}$", RegexOptions.Compiled))
                    {
                        //cardType = "enRoute";
                        cardType = TRANSACTION.CONST_TENDER_OTHER;
                    }
                    else if (Regex.IsMatch(number, @"^(3\d{4}|2131|1800)\d{11}$", RegexOptions.Compiled))
                    {
                        cardType = TRANSACTION.CONST_TENDER_JCB;
                    }
                    else if (Regex.IsMatch(number, @"^56(10\d\d|022[1-5])\d{10}$", RegexOptions.Compiled))
                    {
                        //cardType = "BankCard";
                        cardType = TRANSACTION.CONST_TENDER_OTHER;
                    }
                }
            }

            return cardType;
        }
    }
}
